using System;
using System.Collections.Generic;
using OhioState.Graphics;
using OhioState.Graphics.Proxy;


namespace OhioState.Graphics.OpenGL
{
    public class MaterialHandler : OpenGLObject, IMaterial
    {
        #region IMateral properties
        public IRasterOps Compositor
        {
            get { return _materialProxy.Compositor; }
            set { _materialProxy.Compositor = value; }
        }
        public IShaderProgram Shader
        {
            get { return _materialProxy.Shader; }
            set { _materialProxy.Shader = value; }
        }
        public MaterialColors FrontMaterial
        {
            get { return _materialProxy.FrontMaterial; }
            set { _materialProxy.FrontMaterial = value; }
        }
        public MaterialColors BackMaterial
        {
            get { return _materialProxy.BackMaterial; }
            set { _materialProxy.BackMaterial = value; }
        }
        public bool ApplyLighting
        {
            get { return _materialProxy.ApplyLighting; }
            set { _materialProxy.ApplyLighting = value; }
        }
        public OSUColor CurrentColor
        {
            get { return _materialProxy.CurrentColor; }
            set { _materialProxy.CurrentColor = value; }
        }
        //debug
        private LightCollectionHandler _lightCollectionHandler;
        public LightCollectionHandler LightCollectionHandler
        {
            get { return _lightCollectionHandler; }
            set { _lightCollectionHandler = value; }
        }

        //end of debug
        private bool _created = false;
        public void MakeActive(IRenderPanel panel)
        {
            if (!_created)
            {
                _realMaterial = new MaterialGL();
                _created = true;
                this.Invalid = true;
            }
            //
            // A change request may come in while we are copying stuff over, so
            // we need to copy over the invalid flag and deal with it.
            // Probably not 100% safe, but should get us by. Note, that this 
            // routine is thread-safe, since only the graphics thread should be calling it.
            // The most serious error would be that the uniform variable mapping could change
            // after we link the shader. Fortunately, OpenGL is tolerant on this.
            //
            bool invalid = this.Invalid;
            this.Invalid = false;
            if (invalid)
            {
                //
                // Copy over the information from the proxy object.
                // This may be expensive if the proxy object is on a 
                // remote machine. Hence, the copy avoids having to perform
                // this operation at every frame redraw.
                //
                RasterOpsProxy rasterOps = new RasterOpsProxy();
                rasterOps.Blending = _materialProxy.Compositor.Blending;
                rasterOps.AlphaTest = _materialProxy.Compositor.AlphaTest;
                rasterOps.DepthTest = _materialProxy.Compositor.DepthTest;
                _realMaterial.Compositor = rasterOps;

                _realMaterial.FrontMaterial = _materialProxy.FrontMaterial.Clone() as MaterialColors;
                _realMaterial.BackMaterial = _materialProxy.BackMaterial.Clone() as MaterialColors;
                _realMaterial.ApplyLighting = _materialProxy.ApplyLighting;
                _realMaterial.CurrentColor = new OSUColor(_materialProxy.CurrentColor.A, _materialProxy.CurrentColor.R, _materialProxy.CurrentColor.G, _materialProxy.CurrentColor.B);

                //
                // Replace / update the shader
                //
                _realMaterial.Shader = panel.ResourceManager.GetItem(_materialProxy.Shader as ShaderProgram) as ShaderProgramHandler;
                setShaderProgram(panel);
                updateUniformVariables(panel);
                BuildTextureBindingMap(panel.ResourceManager);
                //_internalShader = _materialProxy.Shader;

                //debug
                //_lightCollectionHandler.LightProxy = _materialProxy.LightCollection;
                //_lightCollectionHandler.RenderPanel = panel;

                //_realMaterial.LightCollectionHandler = new LightCollectionHandler(_materialProxy.LightCollection);
                _realMaterial.LightCollectionHandler = panel.ResourceManager.GetItem(_materialProxy.LightCollection) as LightCollectionHandler;
                //if (_realMaterial.LightCollectionHandler == null)
                    //throw new ApplicationException("The LightCollectionHandler is null");
                
                //_realMaterial.LightCollectionHandler = _lightCollectionHandler;//not needed? seems that the 
                //first assignment line does the job...
                //end of debug
            }

            // TODO remove the next two lines.
            setShaderProgram(panel);
            updateUniformVariables(panel);
            _realMaterial.MakeActive(panel);
        }
        public void Deactivate(IRenderPanel panel)
        {
            _realMaterial.Deactivate(panel);
        }
        private List<ITexture> _textureBinding;
        #endregion IMaterial properties


        private MaterialProxy _materialProxy;
        private MaterialGL _realMaterial;
        public MaterialHandler(MaterialProxy materialProxy)
            : base(materialProxy)
        {
            _materialProxy = materialProxy;
            _textureBinding = new List<ITexture>(_materialProxy.UniformVariableValues.Count);
            //debug
            if( materialProxy.LightCollection != null)
                _lightCollectionHandler = new LightCollectionHandler(materialProxy.LightCollection);
            //end of debug
        }
        //
        // TODO: Remove this junk
        //
        private void setShaderProgram(IRenderPanel panel)
        {
            if (_realMaterial.Shader != null)
            {
                _realMaterial.Shader.MakeActive(panel);
            }
        }
        private void updateUniformVariables(IRenderPanel panel)
        {
            GLShader.SetUniformVariables(panel, this._realMaterial.Shader as IOpenGLResource, _materialProxy.UniformVariableValues);
        }
        private void BuildTextureBindingMap(IRenderResourceManager resourceManager)
        {
            _textureBinding.Clear();

            foreach (String key in _materialProxy.UniformVariableValues.Keys)
            {
                UniformVariableInfo.UniformType type = _materialProxy.UniformVariableValues[key].Type;
                if (type == UniformVariableInfo.UniformType.SAMPLER_1D ||
                    type == UniformVariableInfo.UniformType.SAMPLER_2D ||
                    type == UniformVariableInfo.UniformType.SAMPLER_3D ||
                    type == UniformVariableInfo.UniformType.SAMPLER_CUBE ||
                    type == UniformVariableInfo.UniformType.SAMPLER_1D_SHADOW ||
                    type == UniformVariableInfo.UniformType.SAMPLER_2D_SHADOW)
                {
                    int size = _materialProxy.UniformVariableValues[key].Size;
                    for (int i = 0; i < size; i++)
                    {
                        TextureHandler texture = resourceManager.GetItem(_materialProxy.UniformVariableValues[key][i].Sampler) as TextureHandler;
                        if (texture != null)
                        {
                            _textureBinding.Add(texture);
                        }
                    }
                }
            }
            _realMaterial.TextureBinding = _textureBinding;
        }
    }
}
